/**
 * Copyright 2010 - YangJiandong(chunquedong)
 * 
 * This file is part of ChunMap project
 * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE(Version >=3)
 * 
 * ChunMap是开源软件。你可以自由复制、传播本项目的下载包文件，但必须保持其完整性。
 * 我们不用对使用中的风险及由此造成的损失承担任何责任。
 * 详细情况请见《ChunMap许可协议》。
 * 
 * 想了解更多有关ChunMap的信息，请访问http://code.google.com/p/chunmap/
 * 
 * 下面此段程序作者YangJianDong(chunquedong)
 * 最后修改时间2010.7.17
 */
package chunmap.view;

import chunmap.model.coord.Coordinate2D;
import chunmap.model.coord.Position;
import chunmap.model.coord.Transform;
import chunmap.model.elem.Envelope;
import chunmap.model.elem.EnvelopeBuilder;

public class ViewPort implements View{
    private double _scale;
    private EnvelopeBuilder _viewEnvelop;
    private double _buffer;
    private EnvelopeBuilder _bufferEnvelop;
    private Position _center;
    private int _viewHeight;//屏幕高
    private int _viewWidth;//屏幕宽

    public ViewPort(int width, int height, double buffer)
    {
        _viewHeight = height;
        _viewWidth = width;
        _scale = 1f;
        _viewEnvelop = new EnvelopeBuilder(0, 0, width, height);
        setBufferEnvelope();
        _center = _viewEnvelop.getCenter();
        _buffer = buffer;
    }

    //------------------------------------------------------------------------setter
    public int getWidth() { return _viewWidth; }
    public int getHeight() { return _viewHeight; }
    public void resetSize(int width, int height)
    {
        _viewHeight = height;
        _viewWidth = width;
        computerEnvelope();
    }
    public Envelope getBufferEnvelop() { return _bufferEnvelop; }
    private void setBufferEnvelope()
    {
        _bufferEnvelop = _viewEnvelop.clone();
        _bufferEnvelop.bufferEnvelop(this.dis2World(_buffer));
    }

    public double getScale(){ return _scale; }
    public void setScale(double value) {
    	_scale = value; 
    	computerEnvelope(); 
    }
    public Envelope getViewEnvelop(){ return _viewEnvelop; }
    public void setViewEnvelop(Envelope value)
        {
            if (value == null) return;

            if (value.getWidth() == 0 || value.getHeight() == 0)
            {
                //因为点的边框宽度无穷小
                EnvelopeBuilder eb= new EnvelopeBuilder(value);
                eb.bufferEnvelop(0.1);
                value = eb;
            }
            _scale = computeScale(value);
            _center = value.getCenter();
            computerEnvelope();
        }
    public Position getCenter(){ return _center; }
    public void setCenter(Position value)
        {
            double dx = value.getX() - _center.getX();
            double dy = value.getY() - _center.getY();

            _center = new Coordinate2D(value.getX(), value.getY());
            _viewEnvelop.moveEnvelop(dx, dy);
            _bufferEnvelop.moveEnvelop(dx, dy);
        }

    private void computerEnvelope()
    {
        double x = _center.getX();
        double y = _center.getY();
        double dx = (_viewWidth / _scale) / 2d;
        double dy = (_viewHeight / _scale) / 2d;

        _viewEnvelop = new EnvelopeBuilder(x - dx, y - dy, x + dx, y + dy);
        setBufferEnvelope();
    }
    private double computeScale(Envelope envelop)
    {
        double expectedEnvelopHeight = envelop.getMaxY() - envelop.getMinY();
        double expectedEnvelopWidth = envelop.getMaxX() - envelop.getMinX();
        double s1 = _viewHeight / expectedEnvelopHeight;
        double s2 = _viewWidth / expectedEnvelopWidth;

        return Math.min(s1, s2);
    }

    //------------------------------------------------------------------------util
    public void zoom(double s, double x, double y)
    {
        //world
        double dx0 = x - this.getCenter().getX();
        double dy0 = y - this.getCenter().getY();

        //view
        double dx = dx0 * this.getScale();
        double dy = dy0 * this.getScale();

        this.setCenter( new Coordinate2D(x, y));
        this.setScale(this.getScale() * s);

        //world
        double xx = dx / this.getScale();
        double yy = dy / this.getScale();

        this.setCenter ( new Coordinate2D(x - xx, y - yy));
    }

    //#region 坐标转换
    //------------------------------------------------------------------------coordinateTransform

    public double x2Screen(double x)
    {
        double newX;
        newX = x - getViewEnvelop().getMinX();
        newX = newX * getScale();
        return newX;
    }
    public double y2Screen(double y)
    {
        double newY;
        newY = getViewEnvelop().getMaxY() - y;
        newY = newY * getScale();
        return newY;
    }
    public Transform world2Screen()
    {
        Transform t = new Transform(){
			@Override
			public Position convert(Position p) {
				 double x = x2Screen(p.getX());
		         double y = y2Screen(p.getY());
		         return new Coordinate2D(x, y);
			}};
        return t;
    }
    

    public double x2World(double x)
    {
        double newX;
        newX = x / getScale();
        newX = newX + getViewEnvelop().getMinX();

        return newX;
    }
    public double y2World(double y)
    {
        double newY;
        newY = y / getScale();
        newY = getViewEnvelop().getMaxY() - newY;

        return newY;
    }
    public Transform screen2World()
    {
        Transform t = new Transform(){
			@Override
			public Position convert(Position p) {
				 double x = x2World(p.getX());
		         double y = y2World(p.getY());
		         return new Coordinate2D(x, y);
			}};
        return t;
    }

    public double dis2Screen(double d)
    {
        return d * getScale();
    }
    public double dis2World(double d)
    {
        return d / getScale();
    }
    //#endregion
}
